<html>
    <head>
        <title>打包和压缩前端资源</title>
    </head>
    <body>
        <script>
            // Asp.net core mvc 前端静态资源的打包和压缩

                    /*
                            打包和缩小客户端资源（JavaScript和CSS文件）的方法有很多。

                            最常见的方法是：

                                    1、使用Bundler&MinifierVisual Studio扩展或NuGet包。

                                    2、使用Gulp/Grunt任务管理器及其插件。

                            ABP提供了一种简单、动态、强大、模块化和内置的方式进行前端静态资源的打包盒压缩。
                    */

                    /*
                        一、"Volo.Abp.AspNetCore.Mvc.UI.Bundling"  NuGet包

                                默认情况下，此软件包已与启动模板一起安装。因此，大多数时候，您不需要手动安装它。

                                如果您不使用启动模板，您可以使用ABP CLI将其安装到您的项目中。在包含项目的. csproj文件的文件夹中执行以下命令：

                                    "abp add-package Volo.Abp.AspNetCore.Mvc.UI.Bundling"
                                
                    */

                    /*
                        二、Razor 打包标签助手   Razor Bundling Tag Helpers 


                                1、如何使用bundle标签助手创建捆绑包
                                                创建一个捆绑包的最简单方法是使用 abp-script-bundle 和 abp-style-bundle 这两个标签助手：

                                                        <abp-style-bundle name="MyGlobalBundle">
                                                            <abp-style src="/libs/bootstrap/css/bootstrap.css" />
                                                            <abp-style src="/libs/font-awesome/css/font-awesome.css" />
                                                            <abp-style src="/libs/toastr/toastr.css" />
                                                            <abp-style src="/styles/my-global-style.css" />
                                                        </abp-style-bundle>

                                                此捆绑包定义了一个具有唯一名称的CSS捆绑包： MyGlobalBundle。
                                                
                                                很容易理解如何使用它。让我们看看它是如何工作的：

                                                        ①、ABP在第一次请求时从提供的文件中创建包为惰性。
                                                        
                                                                对于后续调用，它会从缓存返回。
                                                                
                                                                这意味着如果您有条件地将文件添加到包中，它只执行一次，条件的任何更改都不会影响下一个请求的包。

                                                        ②、ABP将捆绑文件单独添加到development环境的页面。它会自动为其他环境捆绑和压缩（staging、production…）。

                                                        ③、捆绑文件可以是物理文件或虚拟/嵌入式文件。

                                                        ④、ABP自动将版本查询字符串添加到捆绑包文件URL，以防止浏览器在捆绑包更新时还使用缓存。

                                                                （如？_v=67872834243042-从相关文件的最后更改日期生成）。即使捆绑包文件被单独添加到页面（在开发环境中），版本控制也有效。


                                2、 导入捆绑标签助手

                                            默认情况下，这已经与启动模板一起导入。因此，大多数时候，您不需要手动添加它。

                                            为了使用bundle标签助手，您需要将其添加到_ViewImports.cshtml文件或页面中：
                                            
                                                @addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling


                                3、未命名捆绑包 ：  Unnamed Bundles 

                                            name 是Rzaor bundle标签助手的"可选属性"。

                                            如果你不定义捆绑包名称，它会根据使用的捆绑文件名自动计算（它们是连接的和散列的）。
                                            
                                            eg ：

                                                  <abp-style-bundle>
                                                        <abp-style src="/libs/bootstrap/css/bootstrap.css" />
                                                        <abp-style src="/libs/font-awesome/css/font-awesome.css" />
                                                        <abp-style src="/libs/toastr/toastr.css" />
                                                        @if (ViewBag.IncludeCustomStyles != false)
                                                        {
                                                            <abp-style src="/styles/my-global-style.css" />
                                                        }
                                                </abp-style-bundle>   
                                                
                                            这可能会创建两个不同的包（一个包含my-global-style.css，另一个不包含）。

                                            ★、未命名包的优点：
                                            
                                                    可以有条件地将项目添加到包中。但这可能会导致基于条件的包的多种变化。
                                            
                                            ★、命名包的有点：

                                                    其他模块可以通过其名称为包贡献（请参阅下面的部分）。


                                4、单个文件捆绑包

                                         如果您只需要将单个文件添加到页面，您可以使用abp-script或abp-style标记，

                                         而无需在abp-script-bundle或abp-style-bundle标记中进行包装。
                                         
                                         eg：
                                                
                                                <abp-script src="/scripts/my-script.js" />


                                        对于上面的示例，捆绑包名称将是"scripts.my-script.js"（“/”替换为 "."). 
                                        
                                        所有捆绑功能也可以按预期用于单个文件捆绑包。
                    */

                    /*
                        三、捆绑选项

                                  如果您需要在多个页面中使用相同的包，或者想要使用更多强大的功能，您可以在模块类中通过代码配置捆绑打包。

                                       1、创建新捆绑包：

                                                eg： 在模块类中配置捆绑包

                                                        [DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))]
                                                        public class MyWebModule : AbpModule
                                                        {
                                                            public override void ConfigureServices(ServiceConfigurationContext context)
                                                            {
                                                                Configure<AbpBundlingOptions>(options =>
                                                                {
                                                                    options
                                                                        .ScriptBundles
                                                                        .Add("MyGlobalBundle", bundle => {
                                                                            bundle.AddFiles(
                                                                                "/libs/jquery/jquery.js",
                                                                                "/libs/bootstrap/js/bootstrap.js",
                                                                                "/libs/toastr/toastr.min.js",
                                                                                "/scripts/my-global-scripts.js"
                                                                            );
                                                                        });                
                                                                });
                                                            }
                                                        }  


                                                备注： 您可以对脚本和样式包使用相同的名称（此处为MyGlobalBundle），因为它们被添加到不同的集合（ScriptBundles和StyleBundles）。

                                                定义这样一个捆绑包后，可以使用上面定义的相同标签助手将其包含到页面中。
                                                
                                                例子：    <abp-script-bundle name="MyGlobalBundle" />  // 意思是直接引用捆绑包

                                                (这一次，捆绑标签助手中没有定义要引用哪些文件，因为捆绑文件是由代码定义的。)


                                      2、配置现有捆绑包

                                                ABP也支持模块化进行捆绑。

                                                模块可以修改由依赖模块创建的现有捆绑包。
                                                
                                                例子：
                                                        [DependsOn(typeof(MyWebModule))]
                                                        public class MyWebExtensionModule : AbpModule
                                                        {
                                                            public override void ConfigureServices(ServiceConfigurationContext context)
                                                            {
                                                                Configure<AbpBundlingOptions>(options =>
                                                                {
                                                                    options
                                                                        .ScriptBundles
                                                                        .Configure("MyGlobalBundle", bundle => {
                                                                            bundle.AddFiles(
                                                                                "/scripts/my-extension-script.js"
                                                                            );
                                                                        });
                                                                });
                                                            }
                                                        }


                                                你还可以使用　ConfigureAll方法来配置所有现有的捆绑包：

                                                        [DependsOn(typeof(MyWebModule))]
                                                        public class MyWebExtensionModule : AbpModule
                                                        {
                                                            public override void ConfigureServices(ServiceConfigurationContext context)
                                                            {
                                                                Configure<AbpBundlingOptions>(options =>
                                                                {
                                                                    options
                                                                        .ScriptBundles
                                                                        .ConfigureAll(bundle => {
                                                                            bundle.AddFiles(    // 添加到我的所有捆绑包中

                                                                                "/scripts/my-extension-script.js"
                                                                            );
                                                                        });
                                                                });
                                                            }
                                                        }

                                                
                    */

                    /*
                       四、捆绑贡献者   Bundle Contributors 

                                将文件添加到现有捆绑包似乎很有用。

                                1、如何创建捆绑贡献者？

                                            如果您需要替换包中的文件，或者您想有条件地添加文件怎么办？定义包贡献者为这种情况提供了额外的功能。

                                            eg: 将bootstrap. css替换为自定义版本的示例捆绑贡献者：

                                                    public class MyExtensionGlobalStyleContributor : BundleContributor // 继承 BundleContributor 捆绑贡献者基类

                                                    {
                                                        public override void ConfigureBundle(BundleConfigurationContext context)
                                                        {
                                                            context.Files.ReplaceOne(  // 针对所有捆绑包，替换bootstrap.css为自定义版本

                                                                "/libs/bootstrap/css/bootstrap.css",
                                                                "/styles/extensions/bootstrap-customized.css"
                                                            );
                                                        }
                                                    }


                                            然后你可以像下面一样使用这个贡献者：

                                                    
                                                    services.Configure<AbpBundlingOptions>(options =>
                                                    {
                                                        options
                                                            .ScriptBundles
                                                            .Configure("MyGlobalBundle", bundle => {
                                                                bundle.AddContributors(typeof(MyExtensionGlobalStyleContributor));  // 注册捆绑贡献者
                                                            });
                                                    });

                                            您还可以在创建新捆绑包时添加贡献者。、

                                            贡献者也可以在捆绑标签助手中使用。
                                            
                                            eg ：

                                                        <abp-style-bundle>
                                                            <abp-style type="@typeof(BootstrapStyleContributor)" />
                                                            <abp-style src="/libs/font-awesome/css/font-awesome.css" />
                                                            <abp-style src="/libs/toastr/toastr.css" />
                                                        </abp-style-bundle>

                                            abp-style和abp-script标签可以获得type属性（而不是src属性），如本示例所示。
                                            
                                            当您添加包贡献者时，它的依赖项也会自动添加到包中。


                                2、捆绑贡献者依赖

                                            捆绑包贡献者可以对其他贡献者有一个或多个依赖关系。
                                            
                                            eg：
                                                [DependsOn(typeof(MyDependedBundleContributor))] //Define the dependency
                                                public class MyExtensionStyleBundleContributor : BundleContributor
                                                {
                                                    //...
                                                }   
                                                    
                                            添加包贡献者时，其依赖项会自动递归添加。

                                            通过防止重复，通过依赖顺序添加的依赖项。

                                            即使它们在单独的包中，也会阻止重复。

                                            ABP将所有包组织在一个页面中并消除重复。

                                            创建贡献者和定义依赖关系是"跨不同模块"组织捆绑包创建的一种方式。

                                3、捆绑贡献者扩展

                                            在某些高级场景中，每当使用捆绑贡献者时，您可能需要进行一些额外的配置。

                                            使用扩展贡献者时，贡献者扩展可以无缝工作。

                                            下面的示例为prms. js库添加了一些样式：

                                                  public class MyPrismjsStyleExtension : BundleContributor
                                                {
                                                    public override void ConfigureBundle(BundleConfigurationContext context)
                                                    {
                                                        context.Files.AddIfNotContains("/libs/prismjs/plugins/toolbar/prism-toolbar.css");
                                                    }
                                                } 
                                                    
                                            然后可以配置AbpBundleContributorOptions来扩展现有的PrismjsStyleBundleContributor

                                                Configure<AbpBundleContributorOptions>(options =>
                                                {
                                                    options
                                                        .Extensions<PrismjsStyleBundleContributor>()
                                                        .Add<MyPrismjsStyleExtension>();
                                                });

                                            每当PrismjsStyleBundleContributor被添加到包中时，MyPrismjsStyleExtension也会被自动添加。


                                4、访问IServiceProvider

                                            虽然很少需要，但BundleConfigurationContext有一个ServiceProvider属性，您可以在ConfigureBundle方法中解析服务依赖关系。


                                5、标准包贡献者 Standard Package Contributors

                                           将特定的NPM包资源（js、css文件）添加到包中非常简单。 

                                           例如，你总是为 bootstrap NPM包(标准包)添加 bootstrap.css 文件。

                                           所有标准NPM包都有内置贡献者。例如，如果您的贡献者依赖于引导程序，您可以直接声明它，而不是自己添加bootstrap. css。

                                                    [DependsOn(typeof(BootstrapStyleContributor))] //声明对 bootstrap.css 的依赖
                                                    public class MyExtensionStyleBundleContributor : BundleContributor
                                                    {
                                                        //...
                                                    }

                                            为标准包使用内置贡献者的优点：

                                                    ①、防止您键入无效的资源路径。

                                                    ②、如果资源路径更改（依赖的贡献者将处理它），则防止更改您的贡献者。

                                                    ③、防止多个模块添加重复文件。

                                                    ④、递归管理依赖项（如有必要，添加依赖项的依赖项）。

                                6、Volo. Abp.AspNetCore.Mvc.UI.Packages 包  
                                
                                             这个包已经默认安装在启动模板中。所以，大多数时候，你不需要手动安装它。
                                             
                                             如果您不使用启动模板，您可以使用ABP CLI将其安装到您的项目中。在包含项目的. csproj文件的文件夹中执行以下命令：

                                                    "abp add-package Volo.Abp.AspNetCore.Mvc.UI.Packages"

                                7、捆绑包继承
                                
                                            在某些特定情况下，可能需要创建一个从其他包继承的新包。

                                            从捆绑包继承（递归）继承该捆绑包的所有文件/贡献者。

                                            然后派生的捆绑包可以添加或修改文件/贡献者而无需修改原始捆绑包。
                                            
                                            示例：
                                                            
                                                services.Configure<AbpBundlingOptions>(options =>
                                                {
                                                    options
                                                        .StyleBundles
                                                        .Add("MyTheme.MyGlobalBundle", bundle => {
                                                            bundle
                                                                .AddBaseBundles("MyGlobalBundle") //Can add multiple
                                                                .AddFiles(
                                                                    "/styles/mytheme-global-styles.css"
                                                                );
                                                        });
                                                });
                    */

                    /*
                        五、附加选项   Additional Options
                                                    
                                本节展示了捆绑打包和压缩的其他有用选项：

                                1、捆绑包模式  Bundling Mode
                                
                                            ABP将捆绑文件单独添加到development环境的页面中。它会自动为其他环境捆绑和缩小（staging，production…）。

                                            大多数时候，这是您想要的行为。

                                            但是，在某些情况下，您可能需要手动配置它。
                                            
                                            有四种模式：

                                                    ①、Auto：根据环境自动确定模式。

                                                    ②、None：没有捆绑或压缩。

                                                    ③、Bundle：捆绑但未压缩。

                                                    ④、BundleAndMinify：捆绑和压缩。

                                            您可以在模块的ConfigureServices中配置AbpBundlingOptions：
                                            
                                                        Configure<AbpBundlingOptions>(options =>
                                                        {
                                                            options.Mode = BundlingMode.Bundle;
                                                        });



                                2、忽略特定文件的压缩

                                            可以忽略对特定文件的压缩。

                                            eg:
                                                Configure<AbpBundlingOptions>(options =>
                                                {
                                                    options.MinificationIgnoredFiles.Add("/scripts/myscript.js");
                                                });

                                            给定的文件仍会添加到捆绑包中，但在这种情况下不会缩小。


                                3、异步加载 JavaScript 和  CSS

                                            您可以配置AbpBundlingOptions以异步加载所有或单个js/css文件。

                                            eg:
                                                Configure<AbpBundlingOptions>(options =>
                                                {
                                                    options.PreloadStyles.Add("/__bundles/Basic.Global");
                                                    options.DeferScriptsByDefault = true;
                                                });

                                             输出html：

                                                    <link rel="preload" href="/__bundles/Basic.Global.F4FA61F368098407A4C972D0A6914137.css?_v=637697363694828051" as="style" onload="this.rel='stylesheet'"/>
                                                    <script defer src="/libs/timeago/locales/jquery.timeago.en.js?_v=637674729040000000"><//script>



                                4、外部/CDN文件支持

                                            捆绑系统会自动识别外部/CDN文件并将它们添加到页面中，而无需进行任何更改。

                                            （1）、在AbpBundlingOptions中使用外部/CDN文件：

                                                            Configure<AbpBundlingOptions>(options =>
                                                            {
                                                                options.StyleBundles
                                                                    .Add("MyStyleBundle", configuration =>
                                                                    {
                                                                        configuration
                                                                            .AddFiles("/styles/my-style1.css")
                                                                            .AddFiles("/styles/my-style2.css")
                                                                            .AddFiles("https://cdn.abp.io/bootstrap.css")  //外部cdn文件
                                                                            .AddFiles("/styles/my-style3.css")
                                                                            .AddFiles("/styles/my-style4.css");
                                                                    });

                                                                options.ScriptBundles
                                                                    .Add("MyScriptBundle", configuration =>
                                                                    {
                                                                        configuration
                                                                            .AddFiles("/scripts/my-script1.js")
                                                                            .AddFiles("/scripts/my-script2.js")
                                                                            .AddFiles("https://cdn.abp.io/bootstrap.js") // 外部cdn文件
                                                                            .AddFiles("/scripts/my-script3.js")
                                                                            .AddFiles("/scripts/my-script4.js");
                                                                    });
                                                            });

                                                        输出如下html：

                                                                <link rel="stylesheet" href="/__bundles/MyStyleBundle.EA8C28419DCA43363E9670973D4C0D15.css?_v=638331889644609730" />
                                                                <link rel="stylesheet" href="https://cdn.abp.io/bootstrap.css" />
                                                                <link rel="stylesheet" href="/__bundles/MyStyleBundle.AC2E0AA6C461A0949A1295E9BDAC049C.css?_v=638331889644623860" />

                                                                <script src="/__bundles/MyScriptBundle.C993366DF8840E08228F3EE685CB08E8.js?_v=638331889644937120"></、script>
                                                                <script src="https://cdn.abp.io/bootstrap.js"></、script>
                                                                <script src="/__bundles/MyScriptBundle.2E8D0FDC6334D2A6B847393A801525B7.js?_v=638331889644943970"></、script>
                                            
                                            (2)、在标签助手中使用外部/CDN文件。

                                                        <abp-style-bundle name="MyStyleBundle">
                                                            <abp-style src="/styles/my-style1.css" />
                                                            <abp-style src="/styles/my-style2.css" />
                                                            <abp-style src="https://cdn.abp.io/bootstrap.css" />
                                                            <abp-style src="/styles/my-style3.css" />
                                                            <abp-style src="/styles/my-style4.css" />
                                                        </abp-style-bundle>

                                                        <abp-script-bundle name="MyScriptBundle">
                                                            <abp-script src="/scripts/my-script1.js" />
                                                            <abp-script src="/scripts/my-script2.js" />
                                                            <abp-script src="https://cdn.abp.io/bootstrap.js" />
                                                            <abp-script src="/scripts/my-script3.js" />
                                                            <abp-script src="/scripts/my-script4.js" />
                                                        </abp-script-bundle>

                                                     输出如下html:

                                                            <link rel="stylesheet" href="/__bundles/MyStyleBundle.C60C7B9C1F539659623BB6E7227A7C45.css?_v=638331889645002500" />
                                                            <link rel="stylesheet" href="https://cdn.abp.io/bootstrap.css" />
                                                            <link rel="stylesheet" href="/__bundles/MyStyleBundle.464328A06039091534650B0E049904C6.css?_v=638331889645012300" />

                                                            <script src="/__bundles/MyScriptBundle.55FDCBF2DCB9E0767AE6FA7487594106.js?_v=638331889645050410"></\script>
                                                            <script src="https://cdn.abp.io/bootstrap.js"></\script>
                                                            <script src="/__bundles/MyScriptBundle.191CB68AB4F41C8BF3A7AE422F19A3D2.js?_v=638331889645055490"></\script>
                    */

                       


                    /*
                        七、最佳实践和建议

                                     建议为一个应用程序定义多个包，每个包用于不同的目的。

                                        ①、全局包（Global bundle:）：全局样式/脚本包包含在应用程序的每个页面中。
                                        
                                                                    主题已经定义了全局样式和脚本包。您的模块可以为它们做出贡献。

                                        ②、布局包 (Layout bundles):  这是单个布局的特定捆绑包。
                                                
                                                                    仅包含使用布局的所有页面之间共享的资源。
                                                                    
                                                                    使用捆绑标签助手来创建捆绑包作为一种良好的做法。

                                        ③、模块包 （Module bundles): 用于单个模块页面之间的共享资源。

                                        ④、页面包 (Page bundles) ：  为每个页面创建的特定包。使用捆绑标签助手创建捆绑包作为最佳实践。

                                    
                                    在性能、网络带宽使用和许多捆绑包的数量之间建立平衡。

                                    南阳方城汽车站

                                    （4小时4分 370公里）

                                    商丘市夏邑县 
                                       100公里 1小时20分

                                    徐州市.苹果新天地

                                    洛阳市。洛阳站


                    */
        </script>
    </body>
</html>